import tkinter as tk
from tkinter import ttk
import random
import numpy as np
from scipy.interpolate import CubicSpline

class RandomLineGenerator:
    def __init__(self, root):
        self.root = root
        self.root.title("随机折线/曲线生成器")
        self.root.geometry("800x600")
        self.root.resizable(True, True)

        # 创建主框架
        main_frame = ttk.Frame(root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 配置网格权重
        root.columnconfigure(0, weight=1)
        root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(1, weight=1)

        # 创建控制面板
        control_frame = ttk.LabelFrame(main_frame, text="控制面板", padding="5")
        control_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))

        # 图表类型选择
        self.chart_type = tk.StringVar(value="line")
        ttk.Radiobutton(control_frame, text="折线", variable=self.chart_type, value="line",
                        command=self.update_button).grid(row=0, column=0, padx=5)
        ttk.Radiobutton(control_frame, text="曲线", variable=self.chart_type, value="curve",
                        command=self.update_button).grid(row=0, column=1, padx=5)

        self.generate_button = ttk.Button(control_frame, text="生成随机折线", command=self.generate_line)
        self.generate_button.grid(row=0, column=2, padx=5)

        # 创建画布
        self.canvas = tk.Canvas(main_frame, bg="white", bd=2, relief=tk.SUNKEN)
        self.canvas.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(5, 0))

    def generate_points(self, width, height, margin):
        """生成不重复的x坐标的点"""
        points = []
        used_x = set()  # 用于跟踪已使用的x值

        while len(points) < 16:
            x = random.randint(margin, width - margin)
            # 确保x值不重复
            if x not in used_x:
                used_x.add(x)
                # 翻转Y坐标，使Y值越大点越高
                y = height - random.randint(margin, height - margin)
                points.append((x, y))

        # 按照x值从小到大排序 (SciPy的CubicSpline需要x是递增的)
        points.sort(key=lambda p: p[0])
        return points

    def generate_line(self):
        """生成折线"""
        # 清除画布
        self.canvas.delete("all")

        # 获取画布尺寸
        width = self.canvas.winfo_width()
        height = self.canvas.winfo_height()

        # 如果画布尚未渲染，使用默认尺寸
        if width <= 1:
            width = 700
        if height <= 1:
            height = 450

        # 边距
        margin = 40

        # 生成16个随机点
        points = self.generate_points(width, height, margin)

        # 绘制坐标轴
        self.draw_axes(width, height, margin)

        # 绘制点
        for x, y in points:
            self.canvas.create_oval(x - 3, y - 3, x + 3, y + 3, fill="red", outline="darkred")
            # 显示坐标
            self.canvas.create_text(x, y - 15, text=f"({x}, {height - y})", font=("Arial", 8))

        # 绘制折线
        for i in range(len(points) - 1):
            x1, y1 = points[i]
            x2, y2 = points[i + 1]
            self.canvas.create_line(x1, y1, x2, y2, fill="blue", width=2)

        # 添加标题
        title = "随机折线"
        self.canvas.create_text(width // 2, 20, text=title, font=("黑体", 14, "bold"))

    def generate_curve(self):
        """生成曲线"""
        # 清除画布
        self.canvas.delete("all")

        # 获取画布尺寸
        width = self.canvas.winfo_width()
        height = self.canvas.winfo_height()

        # 如果画布尚未渲染，使用默认尺寸
        if width <= 1:
            width = 700
        if height <= 1:
            height = 450

        # 边距
        margin = 40

        # 生成16个随机点
        points = self.generate_points(width, height, margin)

        # 绘制坐标轴
        self.draw_axes(width, height, margin)

        # 绘制点
        for x, y in points:
            self.canvas.create_oval(x - 3, y - 3, x + 3, y + 3, fill="red", outline="darkred")
            # 显示坐标
            self.canvas.create_text(x, y - 15, text=f"({x}, {height - y})", font=("Arial", 8))

        # 提取x和y坐标
        x_coords = [p[0] for p in points]
        y_coords = [p[1] for p in points]

        # 使用SciPy的CubicSpline生成三次样条曲线
        try:
            # 创建三次样条插值对象
            cs = CubicSpline(x_coords, y_coords)

            # 生成更多的点以获得平滑曲线
            x_new = np.linspace(min(x_coords), max(x_coords), 200)
            y_new = cs(x_new)

            # 将点转换为坐标对
            curve_points = list(zip(x_new, y_new))

        except Exception as e:
            # 插值失败，弹窗提示
            err = tk.Toplevel(self.root)
            err.title("三次样条插值失败")
            tk.Label(err, text=f"错误信息：{e}").grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E))
            return

        # 绘制曲线
        for i in range(len(curve_points) - 1):
            x1, y1 = curve_points[i]
            x2, y2 = curve_points[i + 1]
            self.canvas.create_line(x1, y1, x2, y2, fill="green", width=2)

        # 添加标题
        title = "随机曲线 (三次样条插值)"
        self.canvas.create_text(width // 2, 20, text=title, font=("黑体", 14, "bold"))

    def update_button(self):
        """更新按钮"""
        if self.chart_type.get() == "line":
            self.generate_button.config(text="生成随机折线", command=self.generate_line)
        elif self.chart_type.get() == "curve":
            self.generate_button.config(text="生成随机曲线", command=self.generate_curve)

    def draw_axes(self, width, height, margin):
        """绘制 X 轴与 Y 轴"""
        # 绘制X轴
        self.canvas.create_line(margin, height - margin, width - margin, height - margin, width=2, arrow=tk.LAST)
        # 绘制Y轴
        self.canvas.create_line(margin, height - margin, margin, margin, width=2, arrow=tk.LAST)

        # 添加X轴标签
        self.canvas.create_text(width // 2, height - 10, text="X轴", font=("黑体", 10, "bold"))
        # 添加Y轴标签
        self.canvas.create_text(20, height // 2, text="Y轴", font=("黑体", 10, "bold"), angle=90)


if __name__ == "__main__":
    root = tk.Tk()
    app = RandomLineGenerator(root)
    root.mainloop()